/*!
 * \file mapir.cc
 *
 * \brief Implementation for the MapIR classes
 *
 * \date Started: 8/5/2010
 * \date Last Modified: 10/1/2010
 * \authors Ian Craig
 * \authors Nicholas Jeanette
 *
 * Copyright (c) 2010, Colorado State University <br>
 * All rights reserved. <br>
 * See ../../COPYING for details. <br>
 */

#include <iegenlib/iegenlib.h>
#include <iegen/iegen.h>

#include <iostream>

using iegenlib::EntityExists;
using iegenlib::EntityNotFound;
using iegenlib::StringException;

namespace iegen{

void MapIR::add_symbolic(string name, int lower, int upper){
  Symbolic symbolic(name,lower,upper);
  const bool is_in = names.find(name) != names.end();
  const bool in_map = symbolics.find(name) != symbolics.end();
  if(is_in){//name already in use
    throw EntityExists(name + " already exists in MapIR");
  }
  if(symbolics.empty() || !in_map){
    names.insert(name);
    symbolics.insert(pair<string,Symbolic>(name,symbolic));
  }
}

void MapIR::add_data_aray(string name, Set bound){
  DataArray data_array(name,bound);
  const bool is_in = names.find(name) != names.end();
  const bool in_map = data_arrays.find(name) != data_arrays.end();
  if(is_in){//name already in use
    throw EntityExists(name + " already exists in MapIR");
  }
  if(data_arrays.empty() || !in_map){
    names.insert(name);
    data_arrays.insert(pair<string,DataArray>(name,data_array));
  }
}

void MapIR::add_index_array(string name, Set input, Set output){
  IndexArray index_array(name,input,output);
  const bool is_in = names.find(name) != names.end();
  const bool in_map = index_arrays.find(name) != index_arrays.end();
  if(is_in){//name already in use
    throw EntityExists(name + " already exists in MapIR");
  }
  if(index_arrays.empty() || !in_map){
    names.insert(name);
    index_arrays.insert(pair<string,IndexArray>(name,index_array));
  }
}

void MapIR::add_er_spec(string name, Set input, Set output, Relation rel){
  ERSpec er_spec(name,input,output,rel);
  const bool is_in = names.find(name) != names.end();
  const bool in_map = er_specs.find(name) != er_specs.end();
  if(is_in){//name already in use
    throw EntityExists(name + " already exists in MapIR");
  }
  if(er_specs.empty() || !in_map){
    names.insert(name);
    er_specs.insert(pair<string,ERSpec>(name,er_spec));
  }
}

void MapIR::add_statement(string name, string text, Set space, Relation sc){
  const bool is_in = names.find(name) != names.end();
  const bool in_map = statements.find(name) != statements.end();
  if(is_in){//name already in use
    throw EntityExists(name + " already exists in MapIR");
  }
  if(statements.empty() || !in_map){
    names.insert(name);
    Statement state(name,text, space,sc);
    statements.insert(pair<string,Statement>(name,state));
  }
}


void MapIR::add_access_relation(string statement_name,
                      string name, string data_array_name, Relation it_to_data){

  const bool is_state = statements.find(statement_name) !=statements.end();
  const bool is_in = names.find(name) != names.end();
  if(is_in){
    throw EntityExists(name + " already exists in MapIR");
  }
  if(is_state){
    statements.find(statement_name)->second.add_access_relation(name,
                                                                data_array_name,
                                                                it_to_data);
    names.insert(name);
  }
  else{
    throw EntityNotFound(statement_name + " is not a statement in MapIR");
  }
}


void MapIR::add_data_dep(string name, Relation dep){
  DataDependence data_dep(name,dep);
  const bool is_in = names.find(name) != names.end();
  const bool in_map = data_deps.find(name) != data_deps.end();
  if(is_in){
    throw EntityExists(name + "already exists in MapIR");
  }
  if(data_deps.empty() || !in_map){
    names.insert(name);
    data_deps.insert(pair<string, DataDependence>(name,data_dep));
  }
}


//MapIR get lists
const list<Symbolic> MapIR::get_symbolics() const {
  list<Symbolic> symbolic_list;
  for(map<string,Symbolic>::const_iterator it = symbolics.begin();
    it != symbolics.end(); ++it){
      symbolic_list.push_back(it->second);
  }
  return symbolic_list;
}

const list<DataArray> MapIR::get_data_arrays() const {
  list<DataArray> data_arrays_list;
  for(map<string,DataArray>::const_iterator it = data_arrays.begin();
    it != data_arrays.end(); ++it){
      data_arrays_list.push_back(it->second);
  }
  return data_arrays_list;
}

const list<IndexArray> MapIR::get_index_arrays() const {
  list<IndexArray> index_arrays_list;
  for(map<string,IndexArray>::const_iterator it = index_arrays.begin();
    it != index_arrays.end(); ++it){
      index_arrays_list.push_back(it->second);
  }
  return index_arrays_list;
}

const list<ERSpec> MapIR::get_er_specs() const {
  list<ERSpec> er_specs_list;
  for(map<string,ERSpec>::const_iterator it = er_specs.begin();
    it != er_specs.end(); ++it){
      er_specs_list.push_back(it->second);
  }
  return er_specs_list;
}

const list<Statement> MapIR::get_statements() const {
  list<Statement> statements_list;
  for(map<string,Statement>::const_iterator it = statements.begin();
    it != statements.end(); ++it){
      statements_list.push_back(it->second);
  }
  return statements_list;
}

const list<DataDependence> MapIR::get_data_deps() const {
  list<DataDependence> data_deps_list;
  for(map<string,DataDependence>::const_iterator it = data_deps.begin();
    it != data_deps.end(); ++it){
      data_deps_list.push_back(it->second);
  }
  return data_deps_list;
}


// get individual items
const Symbolic MapIR::get_symbolic(string name) const{
  map<string,Symbolic>::const_iterator it = symbolics.find(name);
  Symbolic ret_sym("",0,0);
  if(it != symbolics.end()){
    ret_sym = it->second;
  }
  else{
    throw EntityNotFound(name + " does not exist in the Symbolic map");
  }
  return ret_sym;
}

const DataArray MapIR::get_data_array(string name) const{

  map<string,DataArray>::const_iterator it = data_arrays.find(name);
  DataArray ret_da("", Set("{[]}"));
  if(it != data_arrays.end()){
    ret_da = it->second;
  }else{
    throw EntityNotFound(name + " does not exist in the DataArray map");
  }
  return ret_da;
}

const IndexArray MapIR::get_index_array(string name) const{

  map<string,IndexArray>::const_iterator it = index_arrays.find(name);
  IndexArray ret_ia("",Set("{[]}"),Set("{[]}"));
  if(it != index_arrays.end()){
    ret_ia = it->second;
  }else{
    throw EntityNotFound(name + " does not exist in the IndexArray map");
  }
  return ret_ia;
}

const ERSpec MapIR::get_er_spec(string name) const{

  map<string,ERSpec>::const_iterator it = er_specs.find(name);
  ERSpec ret_er("",Set("{[]}"),Set("{[]}"), Relation("{[]->[]}"));
  if(it != er_specs.end()){
    ret_er = it->second;
  }else{
    throw EntityNotFound(name + " does not exist in the ERSpec map");
  }
  return ret_er;
}

const Statement MapIR::get_statement(string name) const{

  map<string,Statement>::const_iterator it = statements.find(name);
  Statement ret_st("","",Set("{[]}"), Relation("{[]->[]}"));
  if(it != statements.end()){
    ret_st = it->second;
  }else{
    throw EntityNotFound(name + " does not exist in the Statement map");
  }
  return ret_st;
}

const AccessRelation MapIR::get_access_relation(string statement_name,
                                                string name) const{

  bool statement_exists = this->statements.find(statement_name) !=
                          this->statements.end();
  AccessRelation ret_acr("","",Relation("{[]->[]}"));
  if(statement_exists){
    Statement statement = this->statements.find(statement_name)->second;
    ret_acr = statement.get_access_relation(name);
  }else{
    throw EntityNotFound(statement_name +
                         " does not exist in the Statement map");
  }
  return ret_acr;
}

const DataDependence MapIR::get_data_dep(string name) const{

  map<string,DataDependence>::const_iterator it = data_deps.find(name);
  DataDependence ret_dat("",Relation("{[]->[]}"));
  if(it != data_deps.end()){
    ret_dat = it->second;
  }else{
    throw EntityNotFound(name + " does not exist in the DataDependence map");
  }
  return ret_dat;
}


const Set DataArray::get_bound() const{
  if(bounds.conSize()>1){
    throw StringException("The bounds on the set is grater than 1");
  }
  return bounds;
}

const AccessRelation Statement::get_access_relation(string name) const{

  map<string,AccessRelation>::const_iterator it = access_relations.find(name);
  AccessRelation ret_acr("","",Relation("{[]->[]}"));
  if(it != access_relations.end()){
    ret_acr = it->second;
  }else{
    throw EntityNotFound(name + " does not exist in the AccessRelation map");
  }
  return ret_acr;
}

void Statement::add_access_relation(string name, string data_array_name,
      Relation it_to_data){

  const bool in_map = access_relations.find(name) != access_relations.end();
  if(!in_map){
    AccessRelation access_relation(name,data_array_name,it_to_data);
    access_relations.insert(pair<string,AccessRelation>(name,access_relation));

  }
  else{
    throw EntityExists(name + " already exists in the AccessRelation map");
  }
}

}//end namespace iegen
